package client;

import java.awt.EventQueue;
import java.awt.Font;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.SecureRandom;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

import sameClasses.NetworkPacket;
import sameClasses.Plugin;
import sameClasses.User;


public class mainFrame extends JFrame {

  private static final long serialVersionUID = 1L;
  private JPanel contentPane;
  private JTabbedPane tabbedPane;
  private JTextField usernameField;
  private JPasswordField passwordField;
  private User myUser;
  private Client myClient;
  private boolean isConnected = false;
  private JPasswordField passwordField_1;
  protected JButton btnConnect;
  protected JCheckBox chckbxLocal;
  protected JTextField txtHost;
  protected JTextField txtPort;
  protected JButton btnDisconnect;
  protected PluginManager myManager;
  protected JPanel pluginPane;
  private static EncPwAndSalt myPwAndSalt;
  private boolean exit = false;
  private String keystorefile;
  private String keystorepw;
  private String servercertificate;

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          mainFrame frame = new mainFrame();
          frame.setVisible(true);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }

  public void addTabToFrame(Plugin pluginelem) {
    pluginelem.start(this.myUser);
    tabbedPane.addTab(pluginelem.getName(), pluginelem.gui());
  }

  public void delTabFromFrame(Plugin pluginelem) {
    tabbedPane.removeTabAt(tabbedPane.indexOfTab(pluginelem.getName()));
    pluginelem.stop(this.myUser);
  }

  public boolean getIsConnected() {
    return this.isConnected;
  }

  public void setIsConnected(boolean b) {
    this.isConnected = b;
  }

  public mainFrame() {
    setDefaultCloseOperation(closeWindow(true));
    setTitle("ENS - Encrypted Network Storage");
    int xlength = 1000;
    int ylength = 600;
    // set the window to the center of the screen
    setBounds((int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - (xlength / 2)
    		, (int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - (ylength / 2), xlength, ylength);
    contentPane = new JPanel();
    setContentPane(contentPane);
    setJMenuBar(buildMenubar());
    load();
    // OPTIONEN
    final JPanel loginAndConnectionPane = new JPanel();
    loginAndConnectionPane.setLayout(null);

    chckbxLocal = new JCheckBox("local");
    chckbxLocal.setBounds(241, 207, 115, 23);
    chckbxLocal.setToolTipText("Recommended for home users");
    loginAndConnectionPane.add(chckbxLocal);

    final JLabel lblPort = new JLabel("Port:");
    lblPort.setHorizontalAlignment(SwingConstants.RIGHT);
    lblPort.setBounds(12, 236, 99, 14);
    loginAndConnectionPane.add(lblPort);

    txtHost = new JTextField();
    txtHost.setBounds(122, 208, 100, 20);
    txtHost.setText("");
    loginAndConnectionPane.add(txtHost);
    txtHost.setColumns(10);

    txtPort = new JTextField();
    txtPort.setBounds(122, 233, 100, 20);
    txtPort.setText("27506");
    loginAndConnectionPane.add(txtPort);
    txtPort.setColumns(10);

    btnConnect = new JButton("Connect");
    btnConnect.setBounds(122, 314, 89, 23);
    loginAndConnectionPane.add(btnConnect);

    btnDisconnect = new JButton("Disconnect");
    btnDisconnect.setBounds(221, 314, 89, 23);
    loginAndConnectionPane.add(btnDisconnect);
    btnDisconnect.setEnabled(false);

    JLabel lblHostname = new JLabel("Hostname or IP:");
    lblHostname.setHorizontalAlignment(SwingConstants.RIGHT);
    lblHostname.setBounds(12, 211, 99, 14);
    loginAndConnectionPane.add(lblHostname);

    JLabel lblUsername = new JLabel("Username:");
    lblUsername.setHorizontalAlignment(SwingConstants.RIGHT);
    lblUsername.setBounds(11, 261, 100, 14);
    loginAndConnectionPane.add(lblUsername);

    JLabel lblPassword = new JLabel("User-Password:");
    lblPassword.setHorizontalAlignment(SwingConstants.RIGHT);
    lblPassword.setBounds(12, 286, 99, 14);
    loginAndConnectionPane.add(lblPassword);

    usernameField = new JTextField();
    usernameField.setBounds(122, 258, 100, 20);
    loginAndConnectionPane.add(usernameField);
    usernameField.setColumns(10);

    passwordField = new JPasswordField();
    passwordField.setBounds(122, 283, 100, 20);
    passwordField.setHorizontalAlignment(SwingConstants.LEFT);
    loginAndConnectionPane.add(passwordField);

    JLabel tooltipHostnameOrIp = new JLabel("?");
    tooltipHostnameOrIp.setToolTipText("If you use the ENS on your hostsystem choose local, else enter a DNS name or the IP of the system that hosts the server.");
    tooltipHostnameOrIp.setBackground(SystemColor.scrollbar);
    tooltipHostnameOrIp.setHorizontalAlignment(SwingConstants.CENTER);
    tooltipHostnameOrIp.setFont(new Font("Tahoma", Font.BOLD, 12));
    tooltipHostnameOrIp.setBounds(218, 211, 20, 14);
    loginAndConnectionPane.add(tooltipHostnameOrIp);

    JLabel label = new JLabel("?");
    label
        .setToolTipText("The port that is used to connect to the server. Default is 27506.");
    label.setHorizontalAlignment(SwingConstants.CENTER);
    label.setFont(new Font("Tahoma", Font.BOLD, 12));
    label.setBackground(SystemColor.scrollbar);
    label.setBounds(218, 236, 20, 14);
    loginAndConnectionPane.add(label);

    JLabel label_1 = new JLabel("?");
    label_1
        .setToolTipText("Your username provided to you by the administrator.");
    label_1.setHorizontalAlignment(SwingConstants.CENTER);
    label_1.setFont(new Font("Tahoma", Font.BOLD, 12));
    label_1.setBackground(SystemColor.scrollbar);
    label_1.setBounds(217, 261, 20, 14);
    loginAndConnectionPane.add(label_1);

    JLabel label_2 = new JLabel("?");
    label_2.setToolTipText("Your private password to connect to the server. It is also provided to you by your administrator.");
    label_2.setHorizontalAlignment(SwingConstants.CENTER);
    label_2.setFont(new Font("Tahoma", Font.BOLD, 12));
    label_2.setBackground(SystemColor.scrollbar);
    label_2.setBounds(217, 286, 20, 14);
    loginAndConnectionPane.add(label_2);

    // Listener
    chckbxLocal.addItemListener(new ItemListener() {
      @Override
      public void itemStateChanged(ItemEvent e) {
        if (chckbxLocal.isSelected()) {
          txtHost.setText("localhost");
          txtHost.setEnabled(false);
        } else {
          txtHost.setEnabled(true);
        }
      }
    });

    btnConnect.setVisible(true);
    txtHost.setVisible(true);
    txtPort.setVisible(true);

    btnConnect.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        try {
          keystorefile = "clientKeystore.jks";
          keystorepw = String.copyValueOf(passwordField_1.getPassword());
          store();
        	
          // only username for verification on the server so that he sends us our salt
          myUser = new User(usernameField.getText());
          
          // init the client+clientOrb
          myClient = new Client(txtHost.getText(), Integer.parseInt(txtPort.getText()), keystorefile, keystorepw);
          
          // get the salt from the server and save it to a temporary array
          byte[] usersalt = ((byte[]) ((NetworkPacket) myClient.sendToServer(new NetworkPacket(myUser, 0, 10, new byte[0]))).getData());
          
          // use the salt with the password here to hash/derive our password
          myUser.changePasswordWithSalt(passwordField.getPassword(),usersalt);
          
          // login -> tmp holds the login-success
          boolean clientIsAuthenticated = (Boolean) ((NetworkPacket) myClient.sendToServer(new NetworkPacket(myUser, 0, 11, new byte[0]))).getData();
          if (clientIsAuthenticated) {
            // we can login -> show the encryption details dialog
            askforEncryptionInformation();
          } else {
          }
          // if anything goes wrong -> pop up a new window that tells us the problem
        } catch (Exception e) {
          errorPopup(e.getMessage());
        }
      }
    });

    btnDisconnect.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        // kill all tabs and sync the last data to the server
        myManager.removeTabs();
        try {
          // logout from the server
          myClient.sendToServer(new NetworkPacket(myUser, 0, 12, new byte[0]));
        } catch (IOException e) {
          e.printStackTrace();
        }
        myClient.closeConnection();
        btnConnect.setText("Connect");
        chckbxLocal.setEnabled(true);
        btnConnect.setEnabled(true);
        if (!txtHost.getText().equalsIgnoreCase("localhost")) {
          txtHost.setEnabled(true);
        }
        txtHost.setEnabled(false);
        txtPort.setEnabled(true);
        usernameField.setEnabled(true);
        passwordField.setEnabled(true);
        btnDisconnect.setEnabled(false);
        mainFrame.this.setIsConnected(false);
      }
    });

    // PLUGINS
    contentPane.setLayout(null);
    tabbedPane = new JTabbedPane(SwingConstants.LEFT);
    tabbedPane.setBounds(0, 0, 992, 549);
    tabbedPane.addTab("Login and Connection", loginAndConnectionPane);

    JTextPane txtpnProvideA = new JTextPane();
    txtpnProvideA
        .setText("1. Provide a passwort for your certificate-keystore. Min. 6 chars.\r\n2. Choose the servercertificate provided to you by your administrator.");
    txtpnProvideA.setEnabled(false);
    txtpnProvideA.setEditable(false);
    txtpnProvideA.setBackground(SystemColor.menu);
    txtpnProvideA.setBounds(21, 34, 201, 62);
    loginAndConnectionPane.add(txtpnProvideA);

    JLabel label_3 = new JLabel("Keystore-PW:");
    label_3.setBounds(223, 37, 67, 14);
    loginAndConnectionPane.add(label_3);

    passwordField_1 = new JPasswordField();
    passwordField_1.setBounds(293, 34, 111, 20);
    loginAndConnectionPane.add(passwordField_1);

    JButton button_1 = new JButton("Choose servercertificate");
    button_1.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        JFileChooser fc = new JFileChooser();
        fc.showOpenDialog(loginAndConnectionPane);
        servercertificate = fc.getSelectedFile().getName();
      }
    });
    button_1.setBounds(221, 67, 183, 23);
    loginAndConnectionPane.add(button_1);

    JLabel lblOnlyFirstTime = new JLabel(
        "Only on first use - SSL certificate initialization process");
    lblOnlyFirstTime.setFont(new Font("Tahoma", Font.PLAIN, 16));
    lblOnlyFirstTime.setHorizontalAlignment(SwingConstants.CENTER);
    lblOnlyFirstTime.setBounds(0, 11, 868, 14);
    loginAndConnectionPane.add(lblOnlyFirstTime);

    JLabel label_4 = new JLabel("?");
    label_4
        .setToolTipText("The keystore is used to store the public keys of the servers that you want to connect to.");
    label_4.setHorizontalAlignment(SwingConstants.CENTER);
    label_4.setFont(new Font("Tahoma", Font.BOLD, 12));
    label_4.setBackground(SystemColor.scrollbar);
    label_4.setBounds(403, 37, 20, 14);
    loginAndConnectionPane.add(label_4);

    JButton btnImportSelectedCertificate = new JButton(
        "Trust selected servercertificate");
    btnImportSelectedCertificate.addActionListener(new ActionListener() {

	@Override
      public void actionPerformed(ActionEvent arg0) {
        String command = "cmd /c start cmd /k keytool -import -alias selfsignedserver -file "
            + servercertificate + " -keystore clientKeystore.jks";
        try {
          Process child = Runtime.getRuntime().exec(command);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    });
    btnImportSelectedCertificate.setBounds(221, 101, 183, 23);
    loginAndConnectionPane.add(btnImportSelectedCertificate);

    contentPane.add(tabbedPane);
  }

  private JMenuBar buildMenubar() {
    JMenuBar menubar = new JMenuBar();
    JMenu filemenu = new JMenu("File");
    JMenuItem menuItem = new JMenuItem("Exit");
    filemenu.add(menuItem);
    menuItem.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        closeWindow(true);
      }
    });
    menubar.add(filemenu);
    return menubar;
  }

  private int closeWindow(boolean exit) {
    if ((exit && this.getIsConnected()) || this.getIsConnected()) {
      myManager.removeTabs();
      myClient.closeConnection();
      this.dispose();
    } else if(exit) {
      this.dispose();
    }
    return 3;
  }

  public static void errorPopup(String message) {
    final JFrame popup = new JFrame();
    popup.setTitle("An error occured!");
    final JPanel cont = new JPanel();
    popup.setBounds(300, 100, 600, 250);
    popup.getContentPane().setLayout(null);
    popup.setVisible(true);
    popup.setContentPane(cont);
    cont.setLayout(null);
    JLabel lblerror = new JLabel(message);
    lblerror.setBounds(20, 10, 300, 46);
    lblerror.setHorizontalAlignment(SwingConstants.RIGHT);
    lblerror.setVisible(true);
    cont.add(lblerror);

    JButton btnOK = new JButton("OK");
    btnOK.setBounds(150, 150, 50, 23);
    btnOK.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        popup.dispose();
      }
    });
    btnOK.setVisible(true);
    cont.add(btnOK);
    popup.setContentPane(cont);
  }

  public void askforEncryptionInformation() {
    final JFrame popup = new JFrame();
    popup.setTitle("Set your encryption details");
    final JPanel cont = new JPanel();
    popup.setBounds(300, 100, 600, 250);
    popup.getContentPane().setLayout(null);
    popup.setVisible(true);
    popup.setContentPane(cont);
    cont.setLayout(null);
    JLabel lblencpwField = new JLabel("Encryption-Password:");
    lblencpwField.setBounds(20, 10, 150, 23);
    lblencpwField.setHorizontalAlignment(SwingConstants.RIGHT);
    lblencpwField.setVisible(true);
    cont.add(lblencpwField);
    final JPasswordField encpwField = new JPasswordField();
    encpwField.setBounds(170, 10, 150, 23);
    encpwField.setVisible(true);
    cont.add(encpwField);
    JLabel lblsalt = new JLabel("Your salt:");
    lblsalt.setHorizontalAlignment(SwingConstants.RIGHT);
    lblsalt.setBounds(20, 43, 150, 23);
    lblsalt.setVisible(true);
    cont.add(lblsalt);
    final JTextField saltField = new JTextField();
    saltField.setBounds(170, 43, 150, 23);
    saltField.setVisible(true);
    cont.add(saltField);
    JButton btncreateSalt = new JButton("Generate new salt");
    btncreateSalt.setBounds(350, 43, 150, 23);
    btncreateSalt.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        SecureRandom ranGen = new SecureRandom();
        byte[] tmp = new byte[32]; // 32 Byte = 256 Bit
        ranGen.nextBytes(tmp);
        JFileChooser fc = new JFileChooser();
        fc.showSaveDialog(cont);
        ObjectOutputStream os;
        try {
          os = new ObjectOutputStream(
              new FileOutputStream(fc.getSelectedFile()));
          os.writeObject(tmp);
          os.flush();
          os.close();
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        }
        saltField.setText(new String(tmp));
      }
    });
    btncreateSalt.setVisible(true);
    cont.add(btncreateSalt);

    JButton chooseSaltFile = new JButton("Use existing salt");
    chooseSaltFile.setBounds(350, 76, 150, 23);
    chooseSaltFile.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        JFileChooser fc = new JFileChooser();
        fc.showOpenDialog(cont);
        if (fc.getSelectedFile().exists()) {
          ObjectInputStream is;
          try {
            is = new ObjectInputStream(new FileInputStream(fc.getSelectedFile()));
            byte[] tmp = (byte[]) is.readObject();
            is.close();
            saltField.setText(new String(tmp));
          } catch (FileNotFoundException e) {
            e.printStackTrace();
          } catch (IOException e) {
            e.printStackTrace();
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
        }
      }
    });
    chooseSaltFile.setVisible(true);
    cont.add(chooseSaltFile);

    JButton btnSetEncDet = new JButton("Set encryption details");
    btnSetEncDet.setBounds(150, 150, 200, 23);
    btnSetEncDet.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        mainFrame.setMyPwAndSalt(new EncPwAndSalt(encpwField.getPassword(),saltField.getText().getBytes()));
        myManager = new PluginManager(mainFrame.this, myClient);
        mainFrame.this.setIsConnected(true);
        btnConnect.setText("Connected");
        chckbxLocal.setEnabled(false);
        btnConnect.setEnabled(false);
        txtHost.setEnabled(false);
        txtPort.setEnabled(false);
        usernameField.setEnabled(false);
        passwordField.setEnabled(false);
        btnDisconnect.setEnabled(true);
        popup.dispose();
        Runtime rt = Runtime.getRuntime();
        rt.gc();
      }
    });
    btnSetEncDet.setVisible(true);
    cont.add(btnSetEncDet);
    popup.setContentPane(cont);
  }

  public static EncPwAndSalt getMyPwAndSalt() {
    return mainFrame.myPwAndSalt;
  }

  public static void setMyPwAndSalt(EncPwAndSalt myPwAndSalt) {
    mainFrame.myPwAndSalt = myPwAndSalt;
  }
  
  private boolean store() {
	    try {
	      ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("clientconfig.cfg")));
	      os.writeObject(keystorefile);
	      os.writeObject(keystorepw);
	      os.flush();
	      os.close();
	    } catch (IOException e) {
	      System.err.println(e.toString());
	      return false;
	    }
	    System.out.println("Clientconfig saved to harddisk");
	    return true;
	  }

	  private boolean load() {
	    try {
	      ObjectInputStream is = new ObjectInputStream(new FileInputStream(new File("clientconfig.cfg")));
	      keystorefile = (String) is.readObject();
	      keystorepw = (String) is.readObject();
	      is.close();
	      System.out.println("Clientconfig successfully loaded from clientconfig.cfg");
	      return true;
	    } catch (ClassNotFoundException e) {
	      System.err.println(e.toString());
	    } catch (IOException e) {
	      System.err.println(e.toString());
	    }
	    System.out.println("Clientconfig NOT loaded from clientconfig.cfg");
	    return false;
	  }

}